﻿using CoreLims.Controllers;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static CoreLims.Models.CoreLims.INV;

namespace CoreLims.Models.CoreLims
{
    public static class InventoryExtension
    {
        public static List<Inventory> getInventoryList(this Inventory inv, CoreLimsContext dbContext) =>
            dbContext.Inventory.ToList();

        public static Inventory getInventory(this Inventory inv, CoreLimsContext dbContext, string Id) =>
            dbContext.Inventory.Where(s => s.Id == Id).FirstOrDefault();

        /// <summary>
        /// 根据材料代码获取“正常”状态入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="MaterialCode">材料代码</param>
        /// <returns></returns>
        public static List<Inventory> getEntryByMCode(this Inventory inv, CoreLimsContext dbContext, string MaterialCode) => 
            dbContext.Inventory.Where(s => s.MaterialCode == MaterialCode && s.Identification == "入库" && s.Complete == false)
            .OrderByDescending(x => x.InDate).ToList();

        /// <summary>
        /// 根据材料代码获取入库单历史记录
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="MaterialCode"></param>
        /// <returns></returns>
        public static List<Inventory> getEntryHistoryByMCode(this Inventory inv, CoreLimsContext dbContext, string MaterialCode) =>
            dbContext.Inventory.Where(s => s.MaterialCode == MaterialCode && s.Identification == "入库" && s.Complete == true)
            .OrderByDescending(x => x.InDate).ToList();

        /// <summary>
        /// 根据入库单，查找对应的出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="EntryId">入库单ID</param>
        /// <returns></returns>
        public static List<Inventory> getOutByEntryId(this Inventory inv, CoreLimsContext dbContext, string EntryId) =>
            dbContext.Inventory.Where(s => s.EntryId == EntryId && s.Identification == "出库")
            .OrderByDescending(x => x.InDate).ToList();

        /// <summary>
        /// 根据物料代码查询该物料所有可领用的出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="MaterialCode"></param>
        /// <returns></returns>
        public static List<Inventory> getOutByMaterialCode(this Inventory inv, CoreLimsContext dbContext, string MaterialCode) =>
            dbContext.Inventory.Where(s => s.MaterialCode == MaterialCode && s.Identification == "出库" && s.Status == "正常")
            .OrderBy(x => x.ReceivedQuantity).ToList();

        /// <summary>
        /// 查询出库单对应的领用记录
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="OutInventoryId"></param>
        /// <returns></returns>
        public static List<UseInventory> getUseInventory(this UseInventory useinv, CoreLimsContext dbContext, string OutInventoryId) =>
            dbContext.UseInventory.Where(s => s.OutInventoryId == OutInventoryId)
            .OrderByDescending(x => x.UseDateTime).ToList();

        /// <summary>
        /// 添加入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="MaterialCode">关联的材料代码  对于系统自动处理的留样，可以不关联材料代码，赋值'null'即可。</param>
        /// <param name="inventoryEx">入库单扩展信息  如果没有扩展信息，赋值'null'即可。</param>
        /// <returns></returns>
        public static string AddEntryInventory(this Inventory inv, CoreLimsContext dbContext, InventoryEx inventoryEx = null)
        {
            inv.ReceivedQuantity = inv.AvailableQuantity; //这行代码不能放到copy函数中。更新记录时，不能修改剩余可领数量
            inv.IsReview = INV.Material.getMaterial(dbContext, inv.MaterialCode).IsReview;
            if (inventoryEx != null)
            {
                inventoryEx.InventoryId = inv.Id;
                dbContext.Add(inventoryEx);
            }
            dbContext.Add(inv);
            dbContext.SaveChanges();
            return "添加入库单成功！";
        }

        /// <summary>
        /// 根据试剂配制记录生成入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        private static string AddEntryInventoryByReagentRecord(this Inventory inv, CoreLimsContext dbContext, Reagent.Record record)
        {
            Inventory inventory = new Inventory();
            inventory.OperatorId = record.PreparationById;
            inventory.OperatorName = record.PreparationByName;
            inventory.MaterialCode = record.MaterialCode;
            inventory.MaterialName = record.MaterialName;
            inventory.InventoryNumber = record.Id;
            inventory.EntryQuantity = (decimal)record.PreparationQuantity;
            inventory.EntryUnit = record.QuantityUnit;
            inventory.AvailableQuantity = (decimal)record.PreparationQuantity;
            inventory.AvailableUnit = record.QuantityUnit;
            inventory.Vld = record.ValidityTo;
            inventory.Remind = record.ValidityRemind;
            return inventory.AddEntryInventory(dbContext, null) + "," + inventory.Id;
        }

        /// <summary>
        /// 添加出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="InventoryId">出库单绑定的入库单ID</param>
        /// <returns></returns>
        public static string AddOutInventory(this Inventory inv, CoreLimsContext dbContext, string EntryInventorId)
        {
            try
            {
                Inventory entry = INV.Inventory.getInventory(dbContext, EntryInventorId);
                if (inv.EntryQuantity > entry.ReceivedQuantity)
                    return "出库数量不能大于入库单的剩余可用数量！";

                if (entry.IsReview) //如果为管控物料，出库单将锁定，审批流程走完后才能领用。
                    inv.Status = INV.InventoryStatus.锁定.ToString();

                inv.ReceivedQuantity = inv.AvailableQuantity; //这行代码不能放到copy函数中。更新记录时，不能修改剩余可领数量
                entry.ReceivedQuantity -= inv.EntryQuantity;
                if (entry.ReceivedQuantity == 0)
                {
                    entry.Status = INV.InventoryStatus.核销.ToString();
                    entry.CancelDate = DateTime.Now;
                }

                dbContext.Update(entry);
                dbContext.Add(inv);
                dbContext.SaveChanges();
                return "添加入库单成功！";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 根据试剂配制记录生成出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="record"></param>
        /// <returns></returns>
        private static string AddOutInventoryByReagentRecord(this Inventory inv, CoreLimsContext dbContext, Reagent.Record record)
        {
            Inventory inventory = new Inventory(record.InventoryId);
            inventory.OperatorId = "1";
            inventory.OperatorName = "System";
            inventory.MaterialCode = record.MaterialCode;
            inventory.MaterialName = record.MaterialName;
            inventory.InventoryNumber = record.Id;
            inventory.EntryQuantity = (decimal)record.PreparationQuantity;
            inventory.EntryUnit = record.QuantityUnit;
            inventory.AvailableQuantity = (decimal)record.PreparationQuantity;
            inventory.AvailableUnit = record.QuantityUnit;
            inventory.Vld = record.ValidityTo;
            inventory.Remind = record.ValidityRemind;
            return inventory.AddOutInventory(dbContext, record.InventoryId);
        }

        /// <summary>
        /// 根据试剂配制记录生成相应的 入库单 和 出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="record"></param>
        /// <returns>成功返回“入库单”ID，失败则返回失败消息</returns>
        public static string AutoInventoryByReagentRecord(this Inventory inv, CoreLimsContext dbContext, Reagent.Record record)
        {
            var reid = INV.Inventory.AddEntryInventoryByReagentRecord(dbContext, record);
            if (!reid.Contains("成功"))
                return "添加入库单时失败，请联系管理员！";
            record.InventoryId = reid.Split(',')[1];

            //对于严格管控的物料，不生成出库单，手动出库时会启动审核流程。
            if (!INV.Inventory.getInventory(dbContext, record.InventoryId).IsReview)
            {
                var st = INV.Inventory.AddOutInventoryByReagentRecord(dbContext, record);
                if (!st.Contains("成功"))
                    return "添加出库单时失败，请联系管理员！";
            }

            return "库存单处理成功！";
        }

        /// <summary>
        /// 删除入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static string DelEntryInventory(this Inventory inv, CoreLimsContext dbContext, string Id)
        {
            var inventory = inv.getInventory(dbContext, Id);
            if (inventory != null)
            {
                var list = INV.Inventory.getOutByEntryId(dbContext, inventory.Id);
                if (list.Count > 0)
                    return "该入库单已关联出库记录，不能删除！";
                
                dbContext.Remove(inventory);
                dbContext.SaveChanges();
                return "库存单删除成功！";
            }
            return "没找到指定的库存单！";
        }

        /// <summary>
        /// 删除出库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static string DelOutInventory(this Inventory inv, CoreLimsContext dbContext, string Id)
        {
            var inventory = inv.getInventory(dbContext, Id);
            if (inventory != null)
            {
                var list = INV.UseInventory.getUseInventory(dbContext, Id);
                if (list != null)
                {
                    foreach (var item in list)
                    {
                        dbContext.Remove(item);
                    }
                }

                dbContext.Remove(inventory);
                dbContext.SaveChanges();
                return "出库单删除成功！";
            }
            return "没找到指定的库存单！";
        }

        /// <summary>
        /// 锁定入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static string LockEntryInventory(this Inventory inv, CoreLimsContext dbContext, string Id)
        {
            var inventory = inv.getInventory(dbContext, Id);
            if (inventory != null)
            {
                inventory.Status = InventoryStatus.锁定.ToString() ;
                dbContext.Update(inventory);
                dbContext.SaveChanges();
                return "库存单锁定成功！";
            }
            return "没找到指定的库存单！";
        }

        /// <summary>
        /// 解锁入库单
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="Id"></param>
        /// <returns></returns>
        public static string ReLockEntryInventory(this Inventory inv, CoreLimsContext dbContext, string Id)
        {
            var inventory = inv.getInventory(dbContext, Id);
            if (inventory != null)
            {
                inventory.Status = InventoryStatus.正常.ToString();
                dbContext.Update(inventory);
                dbContext.SaveChanges();
                return "库存单解除锁定状态成功！";
            }
            return "没找到指定的库存单！";
        }

        /// <summary>
        /// 出库单退库操作
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="OutInventoryId"></param>
        /// <returns></returns>
        public static async Task<string> ReturnOutInventory(this Inventory inv, CoreLimsContext dbContext, string OutInventoryId)
        {
            var outinv = INV.Inventory.getInventory(dbContext, OutInventoryId);
            if (outinv == null) {
                return "该出库单没找到！";
            }

            var entry = INV.Inventory.getInventory(dbContext, outinv.EntryId);
            if (entry == null) {
                return "该出库单对应的入库单没找到！";
            }

            if (inv.EntryQuantity > outinv.EntryQuantity) {
                return "退还数量不能大于该出库单的领用数量！";
            } 
            else {
                outinv.EntryQuantity -= inv.EntryQuantity;
                outinv.AvailableQuantity = inv.AvailableQuantity;
                outinv.ReceivedQuantity = inv.ReceivedQuantity;

                entry.ReceivedQuantity += inv.EntryQuantity;
                entry.Status = INV.InventoryStatus.正常.ToString(); // 如果入库单已经核销，此时退库将更改入库单状态,退库的数量可以继续领用
                entry.Complete = false;  
                entry.CancelDate = null;
            }

            try
            {
                dbContext.Update(outinv);
                dbContext.Update(entry);
                await dbContext.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            return "退库成功！";
        }

        /// <summary>
        /// 出库单领用操作
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="other">这个是由领用信息生成的实例，生成该实例一定要包含所有的必要信息</param>
        /// <returns></returns>
        public static async Task<string> UseOutInventory(this UseInventory newUseInventory, CoreLimsContext dbContext)
        {
            try
            {
                var outInventory = INV.Inventory.getInventory(dbContext, newUseInventory.OutInventoryId);
                if (outInventory == null)
                    return "对应的出库单没找到！";

                newUseInventory.MaterialCode = outInventory.MaterialCode;
                newUseInventory.MaterialName = outInventory.MaterialName;
                newUseInventory.Unit = outInventory.AvailableUnit;

                var re = outInventory.ReduceOutReceived(dbContext, newUseInventory.Quantity);
                if (re.Contains("成功"))
                {
                    dbContext.UseInventory.Add(newUseInventory);
                    await dbContext.SaveChangesAsync();
                }
                else
                    return re;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            return "领用成功！";
        }

        /// <summary>
        /// 出库单领用计算领用数量和状态
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="useQuantity">领用数量</param>
        private static string ReduceOutReceived(this Inventory inv, CoreLimsContext dbContext,decimal? useQuantity)
        {
            if (useQuantity == null)
                return "领用数量不能为空！";

            if(useQuantity > inv.ReceivedQuantity)
                return "领用数量不能大于该出库单的剩余可领数量！";

            inv.ReceivedQuantity -= (decimal)useQuantity;
            if (inv.ReceivedQuantity == 0)
            {
                inv.Status = INV.InventoryStatus.核销.ToString();
                inv.Complete = true;
                inv.CancelDate = DateTime.Now;

                var count = dbContext.Inventory.Where(s => s.EntryId == inv.EntryId && s.Complete == false).Count();
                if (count == 1) // 说明未核销的记录就是当前记录
                {
                    var entry = dbContext.Inventory.Where(s => s.Id == inv.EntryId).FirstOrDefault();
                    entry.Complete = true;
                    entry.CancelDate = DateTime.Now;
                    dbContext.Update(entry);
                }
            }

            dbContext.Update(inv);
            return "领用成功！";
        }

        /// <summary>
        /// 根据物料代码，系统自动领用其对应出库单中的库存。
        /// </summary>
        /// <param name="inv"></param>
        /// <param name="dbContext"></param>
        /// <param name="MaterialCode">物料代码</param>
        /// <param name="Dosage">领用数量</param>
        /// <param name="DosageUnit">数量单位</param>
        /// <returns></returns>
        public static string AutoUseOutInventory
            (this Inventory inv, CoreLimsContext dbContext, string MaterialCode, decimal? Dosage, string DosageUnit, string DoMainName, string DoMainID)
        {
            var ulist = inv.getOutByMaterialCode(dbContext, MaterialCode);
            if (ulist == null)
                return "物料代码：" + MaterialCode + " 没有可供领用的库存！";

            var count = ulist.Where(s => s.AvailableUnit == DosageUnit).Sum(s => s.ReceivedQuantity);
            if (Dosage > count)
                return "物料代码：" + MaterialCode + " 可供领用的库存不够本次领用！";

            var re = "";
            foreach (var item in ulist)
            {
                if (item.ReceivedQuantity >= Dosage)
                {
                    re = item.ReduceOutReceived(dbContext, Dosage);
                    if (re.Contains("成功"))
                    {
                        var newUseInventory = new UseInventory();
                        newUseInventory.OutInventoryId = item.Id;
                        newUseInventory.DoMainName = DoMainName;
                        newUseInventory.DoMainId = DoMainID;
                        newUseInventory.MaterialCode = item.MaterialCode;
                        newUseInventory.MaterialName = item.MaterialName;
                        newUseInventory.Quantity = Dosage;
                        newUseInventory.Unit = DosageUnit;
                        newUseInventory.UseId = "1";
                        newUseInventory.UseName = "System";

                        dbContext.UseInventory.Add(newUseInventory);
                        dbContext.SaveChanges();
                    }
                }
                else
                {
                    Dosage = Dosage - item.ReceivedQuantity;
                    re = item.ReduceOutReceived(dbContext, item.ReceivedQuantity);
                }
            }
            return re;
        }

    }

}
